/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.lang;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * 
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 *
 */
public final class Dates {
    private Dates() {}

    public static final String DATE_PATTERN = "yyyy-MM-dd";
    public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String ISO8601_DATE_WITHOUT_ZONE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
    public static final String ISO8601_TIMESTAMP_WITHOUT_ZONE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";

    public static final String YYYYMMDD_PATTERN = "yyyyMMdd";
    public static final String MMDD_PATTERN = "MMdd";

    /**
     * 获取指定日期所在年份的第一天
     * @param date
     * @return
     */
    public static Date yearStart(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(date.getTime());
        int year = calendar.get(Calendar.YEAR);
        calendar.clear();
        calendar.set(Calendar.YEAR, year);
        return calendar.getTime();
    }

    /**
     * 返回代表指定日期的0时的对象
     * @param date
     * @return 返回代表指定日期的0时的对象
     */
    public static Date dateStart(Date date) {
        if (date == null) {
            return date;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    
    /**
     * 计算两日期相差的天数（自然天数），即 to - from
     * 
     * @param from 开始日期
     * @param to 结束日期
     * @return 返回相差的天数，即结束日期减开始日期得到的自然天数
     */
    public static int dayDiff(Date from, Date to) {
        SimpleDateFormat df = new SimpleDateFormat(DATE_PATTERN);
        try {
            from = df.parse(df.format(from));
            to = df.parse(df.format(to));
        } catch (Exception ex) {
            throw new RuntimeException();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(to);
        long toTime = calendar.getTimeInMillis() + calendar.get(Calendar.DST_OFFSET);
        toTime /= 1000;
        calendar.setTime(from);
        long fromTime = calendar.getTimeInMillis() + calendar.get(Calendar.DST_OFFSET);
        fromTime /= 1000;
        long days = Math.round((toTime - fromTime) / (24 * 60 * 60.0));
        return (int)days;
    }

    /**
     * 比较两日期，如果 date1 在 date2 之前、同一天、之后分别返回-1、0、1
     * @param date1
     * @param date2
     * @return 如果 date1 在 date2 之前、同一天、之后分别返回-1、0、1
     */
    public static int dayCompare(Date date1, Date date2) {
        SimpleDateFormat df = new SimpleDateFormat(YYYYMMDD_PATTERN, Locale.US);
        Integer i1 = Integer.valueOf(df.format(date1));
        Integer i2 = Integer.valueOf(df.format(date2));
        return i1.compareTo(i2);
    }

    /**
     * 判断指定的两个日期是否是同一天
     *
     * @param date1
     * @param date2
     * @return 如果是同一天返回true，否则返回false
     */
    public static boolean isSameDay(Date date1, Date date2) {
        if (date1 != null && date2 != null) {
            Calendar c1 = Calendar.getInstance();
            c1.setTime(date1);
            Calendar c2 = Calendar.getInstance();
            c2.setTime(date2);
            
            return (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR))
                    && (c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH))
                    && (c1.get(Calendar.DAY_OF_MONTH) == c2.get(Calendar.DAY_OF_MONTH));
        }
        return false;
    }
    
    /**
     * 判断指定的日期段与指定的月日段是否重叠
     * @param startDate1
     * @param endDate1
     * @param startDate2
     * @param endDate2
     * @param ignoreYearS1 忽略startDate1中的年份
     * @param ignoreYearE1 忽略endDate1中的年份
     * @param ignoreYearS2 忽略startDate2中的年份
     * @param ignoreYearE2 忽略endDate2中的年份
     * @return 重叠返回true，否则返回false
     */
    public static boolean isDateOverlap(Date startDate1, Date endDate1, Date startDate2, Date endDate2,
            boolean ignoreYearS1, boolean ignoreYearE1, boolean ignoreYearS2, boolean ignoreYearE2) {
        int iStartDate1;
        int iStartDate2;
        SimpleDateFormat ymdFmt = new SimpleDateFormat(YYYYMMDD_PATTERN);
        SimpleDateFormat mdFmt = new SimpleDateFormat(MMDD_PATTERN);
        if (ignoreYearS1 != ignoreYearS2) {
            int year;
            if (ignoreYearS1) {
                year = toYear(startDate2);
            } else {
                year = toYear(startDate1);
            }
            iStartDate1 = Integer.parseInt(year + mdFmt.format(startDate1));
            iStartDate2 = Integer.parseInt(year + mdFmt.format(startDate2));
        } else if (ignoreYearS1) { // ignoreYearS1 && ignoreYearS2
            iStartDate1 = Integer.parseInt("1" + mdFmt.format(startDate1));
            iStartDate2 = Integer.parseInt("1" + mdFmt.format(startDate2));
        } else { // !ignoreYearS1 && !ignoreYearS2
            iStartDate1 = Integer.parseInt(ymdFmt.format(startDate1));
            iStartDate2 = Integer.parseInt(ymdFmt.format(startDate2));
        }
        int iEndDate1;
        int iEndDate2;
        if (ignoreYearE1 != ignoreYearE2) {
            int year;
            if (ignoreYearE1) {
                year = toYear(endDate2);
            } else {
                year = toYear(endDate1);
            }
            iEndDate1 = Integer.parseInt(year + mdFmt.format(endDate1));
            iEndDate2 = Integer.parseInt(year + mdFmt.format(endDate2));
        } else if (ignoreYearE1) { // ignoreYearE1 && ignoreYearE2
            iEndDate1 = Integer.parseInt("1" + mdFmt.format(endDate1));
            iEndDate2 = Integer.parseInt("1" + mdFmt.format(endDate2));
        } else { // !ignoreYearE1 && !ignoreYearE2
            iEndDate1 = Integer.parseInt(ymdFmt.format(endDate1));
            iEndDate2 = Integer.parseInt(ymdFmt.format(endDate2));
        }
        return iStartDate1 <= iStartDate2 && iStartDate2 <= iEndDate1
                || iStartDate1 <= iEndDate2 && iEndDate2 <= iEndDate1
                || iStartDate2 < iStartDate1 && iEndDate1 < iEndDate2;
    }
    
    private static int toYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.YEAR);
    }

    public static String format(Date date, String pattern) {
        return new SimpleDateFormat(pattern).format(date);
    }

    public static Date parse(String date, String pattern) throws ParseException {
        return new SimpleDateFormat(pattern).parse(date);
    }

    public static Date parseAdaptive(String dateStr) throws ParseException {
        if (dateStr == null || dateStr.isEmpty()) {
            return null;
        }

        String fm = null;
        if (dateStr.contains(":")) {
            if (dateStr.contains("T")) {
                if (dateStr.contains(".")) {
                    fm = Dates.ISO8601_TIMESTAMP_WITHOUT_ZONE_PATTERN;
                } else {
                    fm = Dates.ISO8601_DATE_WITHOUT_ZONE_PATTERN;
                }
            } else if (dateStr.contains(":")) {
                if (dateStr.contains(".")) {
                    fm = Dates.TIMESTAMP_PATTERN;
                } else {
                    fm = Dates.DATETIME_PATTERN;
                }
            }
        } else if (dateStr.indexOf('-') > 1) {
            fm = Dates.DATE_PATTERN;
        }
        if (fm != null) {
            return new SimpleDateFormat(fm).parse(dateStr);
        } else {
            return new Date(Long.parseLong(dateStr));
        }
    }

}
